package kotlinx.coroutines.script

import sample.ui.Properties
import abc.reflect.App
import kotlinx.coroutines.script.tes.random
import java.util.LinkedList
import java.util.PriorityQueue
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt

const val NONE = 0
const val ROAD = 1
const val TARGET = 2 // 位置
const val BORDER = 3
const val SELF = 4
//const val SELF_DOT = 8
const val PLAN = 5
const val ENEMY = 6
const val BLACKSMITH = 10
const val BLUE_DOOR = 11
const val RED_DOOR = 12
const val NPC = 13
const val RED_DOT = 14
const val NOT_ARRIVED = 15
const val BOX = 16
const val EQUIP = 17

const val step = 5

data class PathNode(
    val x: Int,
    val y: Int,
    var previous: PathNode? = null,
) {
    private val startAngle1: Int?
        get() {
            if (previous == null) return null
            var point2: PathNode? = previous
            var startPoint: PathNode? = previous?.previous ?: return null
            while (startPoint?.previous != null) {
                point2 = startPoint
                startPoint = startPoint.previous
            }
            val p2 = point2 ?: return null
            val sp = startPoint ?: return null
            return App.take().pointToAngle(
                p2.x - sp.x,
                p2.y - sp.y
            )
        }
    private val startAngle2: Int?
        get() {
            if (previous == null) return null
            var point2: PathNode? = previous
            var startPoint: PathNode? = previous?.previous ?: return null
            var vp: PathNode? = startPoint?.previous?.previous
            while (startPoint?.previous != null && vp != null) {
                point2 = startPoint
                startPoint = startPoint.previous
                vp = startPoint?.previous?.previous
            }
            val p2 = point2 ?: return null
            val sp = startPoint ?: return null
            return App.take().pointToAngle(
                p2.x - sp.x,
                p2.y - sp.y
            )
        }
    private val startAngle3: Int?
        get() {
            if (previous == null) return null
            var point2: PathNode? = previous
            var startPoint: PathNode? = previous?.previous ?: return null
            var vp: PathNode? = startPoint?.previous?.previous?.previous
            while (startPoint?.previous != null && vp != null) {
                point2 = startPoint
                startPoint = startPoint.previous
                vp = startPoint?.previous?.previous?.previous
            }
            val p2 = point2 ?: return null
            val sp = startPoint ?: return null
            return App.take().pointToAngle(
                p2.x - sp.x,
                p2.y - sp.y
            )
        }

    val averageMoveAngle: Int? get() {
        val a2 = startAngle2
        val a1 = startAngle1
        val finalA12 = if (a2 == null) a1
        else if (a1 != null) { // a 和 a5 中和
            val maxA = max(a1, a2)
            val minA = min(a1, a2)
            val offsetA = (maxA - minA) / 2
            if (offsetA < 90) minA + offsetA
            else ((minA - (180 - offsetA)) + 360) % 360
        } else null

        val a3 = startAngle3
        val finalA123 = if (a3 == null) finalA12
        else if (finalA12 != null) { // a 和 a5 中和
            val maxA = max(finalA12, a3)
            val minA = min(finalA12, a3)
            val offsetA = (maxA - minA) / 2
            if (offsetA < 90) minA + offsetA
            else ((minA - (180 - offsetA)) + 360) % 360
        } else null

        return finalA123
    }
}

private val DIRECTIONS1_1 = arrayOf(
    intArrayOf(-step, -step),
    intArrayOf(-step, step),
    intArrayOf(step, -step),
    intArrayOf(step, step),
    intArrayOf(-step, 0),
    intArrayOf(step, 0),
    intArrayOf(0, -step),
    intArrayOf(0, step),
)
private val DIRECTIONS1_2 = arrayOf(
    intArrayOf(step, step),
    intArrayOf(step, -step),
    intArrayOf(-step, step),
    intArrayOf(-step, -step),
    intArrayOf(0, step),
    intArrayOf(0, -step),
    intArrayOf(step, 0),
    intArrayOf(-step, 0),
)
private val DIRECTIONS2_1 = arrayOf(
    intArrayOf(-step, 0),
    intArrayOf(step, 0),
    intArrayOf(0, -step),
    intArrayOf(0, step),
    intArrayOf(-step, -step),
    intArrayOf(-step, step),
    intArrayOf(step, -step),
    intArrayOf(step, step),
)
private val DIRECTIONS2_2 = arrayOf(
    intArrayOf(step, 0),
    intArrayOf(-step, 0),
    intArrayOf(0, step),
    intArrayOf(0, -step),
    intArrayOf(-step, step),
    intArrayOf(-step, -step),
    intArrayOf(step, step),
    intArrayOf(step, -step),
)

// 优先斜边移动。优先直边移动。 随机选
private val DIRECTIONS get() = when(random.nextInt(4)) {
    0 -> DIRECTIONS1_1
    1 -> DIRECTIONS1_2
    2 -> DIRECTIONS2_1
    else -> DIRECTIONS2_2
}

private fun findPath(returnMap: Array<IntArray>, start: Pair<Int, Int>, target: Pair<Int, Int>): PathNode? {
    val xSize = returnMap.size
    val ySize = returnMap[0].size
//    val queue = PriorityQueue(Comparator.comparingInt { (_, _, cost): PathNode -> cost })
    val queue = LinkedList<PathNode>()
    val visited = Array(xSize) {
        BooleanArray(ySize)
    }
    // BFS 起始点加入队列，代价为 0
    queue.offer(PathNode(start.first, start.second, null))
    visited[start.first][start.second] = true
    while (!queue.isEmpty()) {
        val current: PathNode = queue.poll() ?: continue
        // 如果到达目标点，构造路径并返回
        if (current.x == target.first && current.y == target.second) {
            return current
        }
        // 遍历所有方向
        for (dir in DIRECTIONS) {
            val newX: Int = current.x + dir[0]
            val newY: Int = current.y + dir[1]
            // 检查是否越界或访问过，且是否可以通行
            if (newX < 0 || newX >= xSize || newY < 0 || newY >= ySize
                || visited[newX][newY]
                || !isOkPoint(returnMap, newX to newY)
            ) {
                continue
            }
            queue.offer(PathNode(newX, newY, current))
            visited[newX][newY] = true
        }
    }
    // 如果无法到达目标点，返回空路径
    return null
}

fun anySurroundSame(map: Array<IntArray>, searchPoint: Pair<Int, Int>, type: Int): Boolean {
    return map[searchPoint.first - step][searchPoint.second - step] == type ||
            map[searchPoint.first - step][searchPoint.second] == type ||
            map[searchPoint.first - step][searchPoint.second + step] == type ||
            map[searchPoint.first][searchPoint.second - step] == type ||
            map[searchPoint.first][searchPoint.second + step] == type ||
            map[searchPoint.first + step][searchPoint.second - step] == type ||
            map[searchPoint.first + step][searchPoint.second] == type ||
            map[searchPoint.first + step][searchPoint.second + step] == type
}

private fun isOkPoint(returnMap: Array<IntArray>, point: Pair<Int, Int>): Boolean {
    if (point.first < 0 || point.first >= returnMap.size) return false
    if (point.second < 0 || point.second >= returnMap[point.first].size) return false
    return returnMap[point.first][point.second] == ROAD ||
            returnMap[point.first][point.second] == TARGET ||
            returnMap[point.first][point.second] == SELF ||
            returnMap[point.first][point.second] == PLAN ||
            returnMap[point.first][point.second] == ENEMY ||
            returnMap[point.first][point.second] == BLACKSMITH ||
            returnMap[point.first][point.second] == BLUE_DOOR ||
            returnMap[point.first][point.second] == RED_DOOR ||
            returnMap[point.first][point.second] == NPC ||
            returnMap[point.first][point.second] == RED_DOT ||
            returnMap[point.first][point.second] == NOT_ARRIVED ||
            returnMap[point.first][point.second] == BOX ||
            returnMap[point.first][point.second] == EQUIP ||
            isOkPointWithBorder(returnMap, point)
}

// 魔域堡垒不能贴边
// 秘境看看不能贴边
private fun isOkPointWithBorder(returnMap: Array<IntArray>, point: Pair<Int, Int>): Boolean {
    if (Properties.scene == C.S_IMM
        || Properties.scene == C.S_JL
        || Properties.scene == C.S_HY) return false // 疯王第1场景需要border衔接.
    return returnMap[point.first][point.second] == BORDER
}

@Deprecated("")
fun thicken(
    data: MutableList<Pair<Int, Int>>,
    type: Int,
    returnMapArray: Array<IntArray>,
    repeat: Int,
): MutableList<Pair<Int, Int>> {
    val waitAppend = mutableListOf<Pair<Int, Int>>()

    data.forEach {
        repeat(repeat) { i ->
            val times = i + 1
            if (it.first - step * times >= step) {
                if (returnMapArray[it.first - step * times][it.second] == ROAD) {
                    returnMapArray[it.first - step * times][it.second] = type
                    waitAppend.add(it.first - step * times to it.second)
                }
            }
            if (it.first + step * times < returnMapArray.size - step) {
                if (returnMapArray[it.first + step * times][it.second] == ROAD) {
                    returnMapArray[it.first + step * times][it.second] = type
                    waitAppend.add(it.first + step * times to it.second)
                }
            }
            if (it.second - step * times >= step) {
                if (returnMapArray[it.first][it.second - step * times] == ROAD) {
                    returnMapArray[it.first][it.second - step * times] = type
                    waitAppend.add(it.first to it.second - step * times)
                }
            }
            if (it.second + step * times < returnMapArray[it.first].size - step) {
                if (returnMapArray[it.first][it.second + step * times] == ROAD) {
                    returnMapArray[it.first][it.second + step * times] = type
                    waitAppend.add(it.first to it.second + step * times)
                }
            }
        }

    }
    data.addAll(waitAppend)
    return waitAppend
}

fun setupElementsByBlock(point: Pair<Int, Int>, type: Int, mapArray: Array<IntArray>, e: Int = 1) {
    val x = point.first
    val y = point.second
    var sX = max(0, x - step * e)
    val endX = min(mapArray.size - 1, x + step * e)
    val yy = max(0, y - step * e)
    val endY = min(mapArray[0].size - 1 , y + step * e)
    while (sX <= endX) {
        var sY = yy
        while (sY <= endY) {
            if (sX >= 0 && sX < mapArray.size && sY >= 0 && sY < mapArray[sX].size) {
                mapArray[sX][sY] = type
            }
            sY += step
        }
        sX += step
    }
}

fun takeTargetBfsPath(
    returnMapArray: Array<IntArray>,
    startPoint: Pair<Int, Int>,
    notArrived: List<Pair<Int, Int>>
): PathNode? {
    notArrived.firstOrNull()?.also {
        return findPath(returnMapArray, startPoint, it)
    }
    return null
}
fun takeTargetAStarPath(
    returnMapArray: Array<IntArray>,
    startPoint: Pair<Int, Int>,
    notArrived: List<Pair<Int, Int>>
): PathNodeAStar? {
    notArrived.firstOrNull()?.also {
        return findPathAStar(returnMapArray, startPoint, it)
    }
    return null
}


@Deprecated("")
fun isNearBorder(point: Pair<Int, Int>, mapArray: Array<IntArray>): Boolean {
    val x = point.first
    val y = point.second
    // 2步step内的点有都算
    val result = (x - step >= 0 && mapArray[x - step][y] == BORDER)
            || (x + step < mapArray.size && mapArray[x + step][y] == BORDER)
            || (y - step >= 0 && mapArray[x][y - step] == BORDER)
            || (y + step < mapArray[x].size && mapArray[x][y + step] == BORDER)
            || (x - step - step >= 0 && mapArray[x - step - step][y] == BORDER)
            || (x - step >= 0 && y - step >= 0 && mapArray[x - step][y - step] == BORDER)
            || (y - step - step >= 0 && mapArray[x][y - step - step] == BORDER)
            || (y - step >= 0 && x + step < mapArray.size && mapArray[x + step][y - step] == BORDER)
            || (x + step + step < mapArray.size && mapArray[x + step + step][y] == BORDER)
            || (x + step < mapArray.size && y + step < mapArray[x + step].size && mapArray[x + step][y + step] == BORDER)
            || (y + step + step < mapArray[x].size && mapArray[x][y + step + step] == BORDER)
            || (x - step >= 0 && y + step < mapArray[x - step].size && mapArray[x - step][y + step] == BORDER)
    return result
}
@Deprecated("")
fun isSingleNotArrived(point: Pair<Int, Int>, mapArray: Array<IntArray>): Boolean {
    val x = point.first
    val y = point.second
    // 2步step内的点有都算
    var count = 0
    if ((x - step >= 0 && mapArray[x - step][y] == TARGET)) count++
    if ((x + step < mapArray.size && mapArray[x + step][y] == TARGET)) count++
    if ((y - step >= 0 && mapArray[x][y - step] == TARGET)) count++
    if ((y + step < mapArray[x].size && mapArray[x][y + step] == TARGET)) count++
    if ((x - step - step >= 0 && mapArray[x - step - step][y] == TARGET)) count++
    if ((x - step >= 0 && y - step >= 0 && mapArray[x - step][y - step] == TARGET)) count++
    if ((y - step - step >= 0 && mapArray[x][y - step - step] == TARGET)) count++
    if ((y - step >= 0 && x + step < mapArray.size && mapArray[x + step][y - step] == TARGET)) count++
    if ((x + step + step < mapArray.size && mapArray[x + step + step][y] == TARGET)) count++
    if ((x + step < mapArray.size && y + step < mapArray[x + step].size && mapArray[x + step][y + step] == TARGET)) count++
    if ((y + step + step < mapArray[x].size && mapArray[x][y + step + step] == TARGET)) count++
    if ((x - step >= 0 && y + step < mapArray[x - step].size && mapArray[x - step][y + step] == TARGET)) count++
    return count < 1
}



////////////////////////////

// 启发式函数：采用对角距离（Chebyshev distance）
// 对角移动代价为 sqrt(2)，直线移动代价为 1
private fun heuristic(x: Int, y: Int, target: Pair<Int, Int>): Double {
    val dx = abs(target.first - x)
    val dy = abs(target.second - y)
    val minVal = min(dx, dy)
    return minVal * sqrt(2.0) + (maxOf(dx, dy) - minVal)
}
private fun findPathAStar(
    returnMap: Array<IntArray>,
    start: Pair<Int, Int>,
    target: Pair<Int, Int>
): PathNodeAStar? {
    val xSize = returnMap.size
    val ySize = returnMap[0].size
    // 记录已经处理过的点
    val closed = Array(xSize) { BooleanArray(ySize) }
    // 记录起点到每个点的最小代价
    val gScore = Array(xSize) { DoubleArray(ySize) { Double.POSITIVE_INFINITY } }
    // 优先队列：根据 f 值从小到大排序
    val openSet = PriorityQueue<PathNodeAStar>(compareBy { it.f })

    // 初始化起点
    val startH = heuristic(start.first, start.second, target)
    val startNode = PathNodeAStar(start.first, start.second, 0.0, startH, null)
    gScore[start.first][start.second] = 0.0
    openSet.offer(startNode)

    while (openSet.isNotEmpty()) {
        val current = openSet.poll() ?: continue

        // 到达目标点，返回路径（可以通过 current.previous 反向构造完整路径）
        if (current.x == target.first && current.y == target.second) {
            return current
        }

        // 如果该点已经处理过，则跳过
        if (closed[current.x][current.y]) continue
        closed[current.x][current.y] = true

        // 遍历8个方向的邻居
        for (dir in DIRECTIONS) {
            val newX = current.x + dir[0]
            val newY = current.y + dir[1]

            // 检查边界
            if (newX < 0 || newX >= xSize || newY < 0 || newY >= ySize) continue
            // 检查是否可以通行
            if (!isOkPoint(returnMap, newX to newY)) continue
            // 如果已经处理过，也跳过
            if (closed[newX][newY]) continue

            // 计算移动代价：对角方向的代价为 sqrt(2)，直线方向为 1
            val moveCost = if (dir[0] != 0 && dir[1] != 0) sqrt(2.0) else 1.0
            val tentativeG = current.cost + moveCost

            // 如果该路径更优，则更新代价，并加入队列
            if (tentativeG < gScore[newX][newY]) {
                gScore[newX][newY] = tentativeG
                val h = heuristic(newX, newY, target)
                val neighbor = PathNodeAStar(newX, newY, tentativeG, tentativeG + h, current)
                openSet.offer(neighbor)
            }
        }
    }
    // 如果无法到达目标，返回 null
    return null
}

data class PathNodeAStar(
    val x: Int,
    val y: Int,
    var cost: Double,       // 从起点到当前节点的实际代价 g
    var f: Double,          // f = g + h，估计总代价
    var previous: PathNodeAStar? = null
)
private val PathNodeAStar.startAngle1: Int?
    get() {
        if (previous == null) return null
        var point2: PathNodeAStar? = previous
        var startPoint: PathNodeAStar? = previous?.previous ?: return null
        while (startPoint?.previous != null) {
            point2 = startPoint
            startPoint = startPoint.previous
        }
        val p2 = point2 ?: return null
        val sp = startPoint ?: return null
        return App.take().pointToAngle(
            p2.x - sp.x,
            p2.y - sp.y
        )
    }
private val PathNodeAStar.startAngle2: Int?
    get() {
        if (previous == null) return null
        var point2: PathNodeAStar? = previous
        var startPoint: PathNodeAStar? = previous?.previous ?: return null
        var vp: PathNodeAStar? = startPoint?.previous?.previous
        while (startPoint?.previous != null && vp != null) {
            point2 = startPoint
            startPoint = startPoint.previous
            vp = startPoint?.previous?.previous
        }
        val p2 = point2 ?: return null
        val sp = startPoint ?: return null
        return App.take().pointToAngle(
            p2.x - sp.x,
            p2.y - sp.y
        )
    }
private val PathNodeAStar.startAngle3: Int?
    get() {
        if (previous == null) return null
        var point2: PathNodeAStar? = previous
        var startPoint: PathNodeAStar? = previous?.previous ?: return null
        var vp: PathNodeAStar? = startPoint?.previous?.previous?.previous
        while (startPoint?.previous != null && vp != null) {
            point2 = startPoint
            startPoint = startPoint.previous
            vp = startPoint?.previous?.previous?.previous
        }
        val p2 = point2 ?: return null
        val sp = startPoint ?: return null
        return App.take().pointToAngle(
            p2.x - sp.x,
            p2.y - sp.y
        )
    }

val PathNodeAStar.averageMoveAngle: Int? get() {
    val a2 = startAngle2
    val a1 = startAngle1
    val finalA12 = if (a2 == null) a1
    else if (a1 != null) { // a 和 a5 中和
        val maxA = max(a1, a2)
        val minA = min(a1, a2)
        val offsetA = (maxA - minA) / 2
        if (offsetA < 90) minA + offsetA
        else ((minA - (180 - offsetA)) + 360) % 360
    } else null

    val a3 = startAngle3
    val finalA123 = if (a3 == null) finalA12
    else if (finalA12 != null) { // a 和 a5 中和
        val maxA = max(finalA12, a3)
        val minA = min(finalA12, a3)
        val offsetA = (maxA - minA) / 2
        if (offsetA < 90) minA + offsetA
        else ((minA - (180 - offsetA)) + 360) % 360
    } else null

    return finalA123
}
